import { _decorator, CCFloat, CCInteger, Component, instantiate, Node, NodePool, ScrollView, UITransform } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('ScrollList')
export class ScrollList extends Component {
	@property(ScrollView)
	private view: ScrollView = null;
	@property(CCInteger)
	private over_item_count: number = 2;
	@property(CCFloat)
	private space_x: number = 0;

	private ui_view: UITransform;
	private ui_content: UITransform;

	private content_top: number;
	private item_height: number;

	private get item_list(){
		return [...this.ui_content.node.children].sort((a, b)=>b.position.y-a.position.y);
	}

	protected onLoad(){
		this.ui_view = this.view.content.parent.getComponent(UITransform);
		this.ui_content = this.view.content.getComponent(UITransform);
		this.content_top = this.ui_content.height * (1-this.ui_content.anchorY);

		this.view.scrollEvents.push(abi.Ab_cc_bA.Ab_creatEventHandle_bA({
			target: this.view.node,
			component: 'ScrollList',
			handler: 'onScroll',
		}));
	}

    start() {
		this.item_height = this.ui_content.node.children[0].getComponent(UITransform).height;
		this.ui_content.node.children.forEach((item, index)=>this.moveItemByIndex(item, index));

		if(this.data_list){
			this.fixItemCount(this.data_list.length);
			this.ui_content.height = this.data_list.length * this.item_height + (this.data_list.length-1) * this.space_x;
		}
    }

	private onScroll(view: ScrollView){
		let y = view.getScrollOffset().y;

		let start_index = Math.ceil(y / this.item_height);
		start_index = Math.max(0, start_index - this.over_item_count);
		let end_index = start_index + this.ui_content.node.children.length - 1;
		end_index = Math.min(this.data_list.length-1, end_index);

		let item_map = this.ui_content.node.children.map(node=>{
			let index = this.data_list.findIndex(el=>String(el[this.data_key])==node.name);
			return {node, index};
		}).sort((a, b)=>a.index-b.index);

		//let min_idx = Math.max(...item_map.map(el=>el.index));
		//let max_idx = Math.min(...item_map.map(el=>el.index));

		item_map
		.filter(el=>el.index < start_index)
		.forEach((el, i, ls)=>{
			let index = start_index + this.ui_content.node.children.length - ls.length + i;
			this.moveItemByIndex(el.node, index);
		});

		item_map
		.filter(el=>el.index > end_index)
		.forEach((el, i)=>{
			let index = end_index - this.ui_content.node.children.length + i + 1;
			this.moveItemByIndex(el.node, index);
		});

		item_map
		.filter(el=>el.index >= start_index && el.index <= end_index)
		.forEach((el, i)=>{
			this.moveItemByIndex(el.node, el.index);
		});
	}

	private item_pool = new NodePool();
	private fixItemCount(count: number){
		let ui_demo = this.ui_content.node.children[0].getComponent(UITransform);
		let full_count = Math.ceil(this.ui_view.height / (ui_demo.height + this.space_x));
		if(count>full_count) count = full_count + this.over_item_count;

		let diff = count - this.ui_content.node.children.length;
		if(diff > 0){
			for(let i=0; i<diff; i++) (this.item_pool.get() ?? instantiate(ui_demo.node)).setParent(this.ui_content.node);
		}else if(diff < 0){
			this.ui_content.node.children.slice(diff).forEach(el=>this.item_pool.put(el));
		}
	}

	private moveItemByIndex(item: Node, index: number){
		let y = this.content_top - (index+0.5) * this.item_height - this.space_x * index;
		item.setPosition(item.position.x, y, item.position.z);

		if(!this.data_list) return void 0;
		let data = this.data_list[index];
		if(data){
			item.name = String(data[this.data_key]);
			this.render_item_call_list.forEach(call=>call(item, this.data_list[index], this.render_item_node_cache));
		}
	}

	private data_list: any[];
	private data_key: string;
	public setDataList<el>(list: el[], key: keyof el){
		this.data_list = list;
		this.data_key = key as string;
		this.fixItemCount(list.length);
		this.ui_content.height = list.length * this.item_height + (list.length-1) * this.space_x;
	}

	private render_item_node_cache: Map<Node, any> = new Map();
	private render_item_call_list: Array<(node: Node, data: any, node_cache: Map<Node, any>)=>any> = [];
	public onRenderItem(call: (node: Node, data: any, node_cache: Map<Node, any>)=>any){
		this.render_item_call_list.push(call);
	}

	public get NodeCache(){
		return this.render_item_node_cache;
	}
}

